package com.wx.spring;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wx.annotation.RpcConsumer;
import com.wx.annotation.RpcProvider;
import com.wx.cache.ConsumerCache;
import com.wx.cache.ExposedInterfaceCache;
import com.wx.cache.ProviderCache;
import com.wx.config.RegisterProperties;
import com.wx.network.client.RpcClientSocket;
import com.wx.network.sever.RpcServerSocket;
import com.wx.reflect.ReflectUtil;
import com.wx.register.RedisRegisterCenter;
import com.wx.register.RegisterMesgPojo;
import com.wx.util.IpUtil;
import com.wx.util.SystemUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.StopWatch;
import redis.clients.jedis.Jedis;

import java.beans.Introspector;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Component
public class RpcServerAutoRegister implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware, EnvironmentAware {
    private BeanDefinitionRegistry beanDefinitionRegistry;
    private ConfigurableListableBeanFactory configurableListableBeanFactory;
    private Environment environment;
    private ApplicationContext applicationContext;
    private Jedis jedis;
    @SneakyThrows
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }

    @SneakyThrows
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {

        RegisterProperties bean = applicationContext.getBean(RegisterProperties.class);
        log.info("获得注册中心地址信息{}",bean.toString());
        log.info("====================================开始注册================================================");
        RedisRegisterCenter.initClient(bean.getRegisterHost(),bean.getRegisterPort());
        jedis = RedisRegisterCenter.getJedis();
        log.info("=====================================结束注册===============================================");
        StopWatch rpc = new StopWatch("rpc");
        log.info("开始初始化生产端服务器");
        rpc.start("scanPackage");
        String consumerPackageName = bean.getConsumerPackageName();
        String providerPackageName = bean.getProviderPackageName();
        //进行扫描获得所有需要注入的dao
        String consumerPath = "classpath*:" + consumerPackageName.replace('.', '/') + "/**/*.class";
        String providerPath = "classpath*:" + providerPackageName.replace('.', '/') + "/**/*.class";
        //bean扫描
        //资源解析器
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
        //开始解析消费者资源包
        ConsumerCache consumerCache = new ConsumerCache();
        Resource[] consymerResources = resourcePatternResolver.getResources(consumerPath);
        for (Resource resource : consymerResources) {
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
            String className = metadataReader.getClassMetadata().getClassName();
            RpcConsumer annotation = ReflectUtil.getAnnotation(className, RpcConsumer.class);
            if(annotation!=null){
                consumerCache.setCache(className,annotation.serverName());
                //对于接口进行代理
                ScannedGenericBeanDefinition beanDefinition = new ScannedGenericBeanDefinition(metadataReader);
                //注入bean定义的资源
                beanDefinition.setResource(resource);
                beanDefinition.setSource(resource);
                beanDefinition.setSource("singleton");
                //设置生成被代理接口类的
                String beanClassName = beanDefinition.getBeanClassName();
                beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
                beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(annotation.serverName());
                //设置生成的bean对象
                beanDefinition.setBeanClass(RpcConsumerInterFaceFactoryBeanProxy.class);

                String beanName = Introspector.decapitalize(ClassUtils.getShortName(beanClassName));
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
                //注入到容器里面
                beanDefinitionRegistry.registerBeanDefinition(beanName,definitionHolder.getBeanDefinition());
            }
        }
        rpc.stop();
        rpc.start("scanProvider");
        Resource[] resources = resourcePatternResolver.getResources(providerPath);
        //获得系统属性
        String serverName = environment.getProperty("spring.application.name");
        ProviderCache providerCache = new ProviderCache();
        for (Resource resource : resources) {
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
            String className = metadataReader.getClassMetadata().getClassName();
            RpcProvider annotation = ReflectUtil.getAnnotation(className, RpcProvider.class);
            if(annotation!=null){
                providerCache.setCache(className,serverName);
                //添加服务接口名与
                String[] split = className.split("\\.");
                int length = split.length;
                String s = split[length - 1];
                String s1 = (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
                Class faceClass = ReflectUtil.getInterFaceClass(className);
                ExposedInterfaceCache.put(s1,faceClass);
            }
        }
        rpc.stop();
        rpc.start("rpcServer");
        Integer port = bean.getServerPort();
        int userfulPort = IpUtil.getUserfulPort(port);
        //如果provider启动不为空就开始启动rpc服务
        if(!providerCache.isEmpty()){
            //获得线程数量
            Integer cpuNumberOfNuclei = SystemUtil.getCpuNumberOfNuclei();
            RpcServerSocket serverSocket = new RpcServerSocket(applicationContext);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //启动netty服务器
                    serverSocket.startRpcServer(userfulPort,cpuNumberOfNuclei/2,cpuNumberOfNuclei);
                }
            }).start();
            //provider把信息注册到redis里面
            ArrayList<String> allKey = providerCache.getAllKey();
            RegisterMesgPojo build = RegisterMesgPojo.builder().exposeClassNames(allKey).serverName(serverName).ip(IpUtil.getLocalIp()).port(userfulPort).build();
            registServerToRedis(build);
        }
        rpc.stop();
        //建立与rpc服务提供者连接
        rpc.start("connectServer");
        connectRpcServer(consumerCache);
        rpc.stop();
       log.info(rpc.prettyPrint());
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        this.configurableListableBeanFactory=configurableListableBeanFactory;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment=environment;
    }
    /**
     * 服务消费者代理类
     */
    public void connectRpcServer(ConsumerCache consumerCache) throws InterruptedException {
        Collection<String> allValues = consumerCache.getAllValues();
        List<RegisterMesgPojo> servers = allValues.stream().distinct().map(t -> {
            String app = jedis.hget("app", t);
            return JSON.parseObject(app, RegisterMesgPojo.class);
        }).collect(Collectors.toList());
        RpcClientSocket socket = new RpcClientSocket();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (RegisterMesgPojo server : servers) {
                    try {
                        socket.connect(server.getIp(),server.getPort(),server.getServerName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

    }

    public void registServerToRedis(RegisterMesgPojo registerMesgPojo){
        String jsonString = JSONObject.toJSONString(registerMesgPojo);

        HashMap<String, String> map = new HashMap<>();
        map.put(registerMesgPojo.getServerName(),jsonString);
        jedis.hmset("app",map);
        log.info("provider提供服务注册到注册中心{}",registerMesgPojo);
    }
}
